
package com.yvon.boot.mybatis;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yvon.boot.mybatis.model.BaseTreeEntity;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Service基类
 * @author aidex
 * @email aidex@qq.com
 * @version 2020-02-27
 */
public class BaseTreeServiceImpl<T extends BaseTreeEntity<T>, M extends BaseMapper<T>> extends BaseServiceImpl<T, M> implements IBaseTreeService<T>{

    @Override
    public boolean saveUpdateNode(T entity) {
        T parent = getById(entity.getParentId());
        // 不存在id则为新增
        if (Objects.isNull(entity.getId())) {
            setTreeProperties(entity, parent, OpType.insert);
            getBaseMapper().insert(entity);
        }else{
            // 存在id则为编辑
            //编辑
            T old = getById(entity.getId());
            if (isMoveToSelfChildNode(entity, old)) {
                throw new RuntimeException( "非法移动，不允许将节点移动到自身或者子节点下");
            }
            if (entity.getParentId().equals(entity.getId())) {
                throw new RuntimeException("修改失败，父级节点不能是自己");
            }
            // 判断是否修改过当前数据的排序号和调整过当前树节点的父级
            if (isUpdateTreeProperties(entity, old)) {
                // 重新设置节点属性值
                setTreeProperties(entity, old, OpType.update);
                // 更新子节点属性值
                updateChildrenTreeProperties(entity, old);
            }
            // 新的父节点变更为非叶子节点
            updateParentLeaf(entity);
            getBaseMapper().updateById(entity);
            // 判断原节点是否还有子节点，否则改为叶子节点
            updateOldParentLeaf(old);
        }
        return Boolean.TRUE;
    }

    /**
     * 根据父ID查询子节点
     *
     * @param parentId 父ID
     * @return List<T>
     * @author : Yvon / 2022-03-12
     */
    @Override
    public List<T> findChildListByParentId(Long parentId) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", parentId);
        return baseMapper.selectList(queryWrapper);
    }

    /**
     * 根据父ID查询子节点的
     * @param parentIds 父IDs
     * @return
     */
    @Override
    public List<T> findChildListByParentIds(String parentIds) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.last("parent_ids like concat("+ parentIds +",'%') and parent_ids &lt;&gt; "+ parentIds);
        return baseMapper.selectList(queryWrapper);
    }


    /**
     * 查询最大序号
     *
     * @param parentId
     * @return Integer
     * @author : Yvon / 2022-03-14
     */
    @Override
    public Integer findMaxSort(Long parentId) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", parentId);
        queryWrapper.orderByDesc("tree_sort");
        queryWrapper.last("LIMIT 1");

        T one = baseMapper.selectOne(queryWrapper);
        if (Objects.nonNull(one)) {
            return one.getTreeSort();
        }else {
            return 0;
        }
    }

    /**
     * 更新下属所有节点的tree_path或者是tree_sorts
     *
     * @param parent
     * @param oldParent
     * @return
     */
    protected List<T> getUpdateChildTreeProperties(T parent, T oldParent) {
        //原来的排序字段和树路径字段
        String oldTreeSorts = oldParent.getTreeSorts();
        String oldParentIds = oldParent.getParentIds();
        //现在的排序字段
        String currentTreeSorts = parent.getTreeSorts();
        String currentParentIds = parent.getParentIds();
        //查找当前节点下的所有子节点
        List<T> list = findChildListByParentIds(oldParentIds);
        for (T dto : list) {
            dto.setParentIds(dto.getParentIds().replace(oldParentIds, currentParentIds));
            dto.setTreeSorts(dto.getTreeSorts().substring(0, oldTreeSorts.length()).replace(oldTreeSorts, currentTreeSorts) + dto.getTreeSorts().substring(oldTreeSorts.length()));
            dto.setTreeLevel(dto.getParentIds().split("/").length);
        }
        return list;
    }

    /**
     * 自动计算树形结构其他属性值
     *
     * @param dto 修改后的对象
     * @param old 原对象（更新方法使用）
     * @return
     */
    protected void setTreeProperties(T dto, T old, OpType opType) {
        //获取父级节点
        T parentDTO = getById(dto.getParentId());
        //非初始化根节点情况
        if (parentDTO != null) {
            //判断操作类型
            switch (opType) {
                case insert:
                    //新增
                    dto.setId(IdWorker.getId());
                    //设置当前树路经
                    dto.setParentIds(parentDTO.getParentIds() + "/" + dto.getId());
                    //设置当前树全路径排序
                    int treeSort = dto.getTreeSort();
                    String treeSorts = String.format("%06d", treeSort);
                    dto.setTreeSorts(parentDTO.getTreeSorts() + "/" + treeSorts);
                    //设置当前是否为叶子节点，新增全部为叶子节点
                    dto.setTreeLeaf(1);
                    //设置当前节点级别
                    dto.setTreeLevel(parentDTO.getTreeLevel() + 1);
                    //判断当前父节点是否为叶子节点，如果是，则更改为非叶子节点
                    if (1 == parentDTO.getTreeLeaf()) {
                        parentDTO.setTreeLeaf(0);
                        updateById(parentDTO);
                    }
                    break;
                case update:
                    //修改
                    //主要针对于排序字段的修改
                    if (isUpdateTreeProperties(dto, old)) {
                        //当前新的本级排序字段
                        String currentTreeSorts = String.format("%06d", dto.getTreeSort());
                        //当前新的总体排序字段
                        String newTreeSorts = parentDTO.getTreeSorts() + "/" + currentTreeSorts;
                        //赋值
                        dto.setTreeSorts(newTreeSorts);
                        //重新生成当前节点树路径
                        String currentParentIds = parentDTO.getParentIds() + "/" + dto.getId();
                        Integer currentTreeLevel = parentDTO.getTreeLevel() + 1;
                        //赋值
                        dto.setParentIds(currentParentIds);
                        dto.setTreeLevel(currentTreeLevel);
                    }
                    break;
                default:
                    break;
            }
        } else {
            //初始化树根节点
            dto.setId(IdWorker.getId());
            dto.setParentIds(String.valueOf(dto.getId()));
            //设置当前树全路径排序
            Integer treeSort = dto.getTreeSort();
            String treeSorts = String.format("%06d", treeSort);
            dto.setTreeSorts(treeSorts);
            // 设置当前是否为叶子节点，新增全部为叶子节点
            if (opType == OpType.insert) {
                dto.setTreeLeaf(1);
            }
            //设置当前节点级别.
            dto.setTreeLevel(1);
        }
    }


    /**
     * 更新子节点
     *
     * @param dto 修改后的对象
     * @param old 源对象
     */
    protected void updateChildrenTreeProperties(T dto, T old) {
        if (isUpdateTreeProperties(dto, old)) {
            List<T> childList = getUpdateChildTreeProperties(dto, old);
            if (!CollectionUtils.isEmpty(childList)) {
                updateBatchById(childList);
            }
        }
    }


    /**
     * 更新父节点变更为非叶子节点
     *
     * @param dto dto
     */
    protected void updateParentLeaf(T dto) {
        T parent = getById(dto.getParentId());
        // 新的父节点变更为非叶子节点
        if (0 != dto.getParentId() && parent.getTreeLeaf() == 1) {
            parent.setTreeLeaf(0);
            getBaseMapper().updateById(parent);
        }
    }

    /**
     * 判断原节点是否还有子节点，否则改为叶子节点
     *
     * @param old 老
     */
    protected void updateOldParentLeaf(T old) {
        Long oldParentId = old.getParentId();
        // 判断原节点是否还有子节点，否则改为叶子节点
        if (!hasChildById(oldParentId)) {
            T oldParent = getById(oldParentId);
            oldParent.setTreeLeaf(1);
            getBaseMapper().updateById(oldParent);
        }
    }

    /**
     * 判断是否修改过当前数据的排序号和调整过当前树节点的父级
     *
     * @param dto
     * @param old
     * @return
     */
    protected boolean isUpdateTreeProperties(T dto, T old) {
        return (!old.getTreeSort().equals(dto.getTreeSort()) || !old.getParentId().equals(dto.getParentId()));
    }

    /**
     * 判断是是否移动节点到自身或者子节点下
     *
     * @param dto 更新对象
     * @param old 源对象
     * @return boolean
     */
    protected boolean isMoveToSelfChildNode(T dto, T old) {
        if (!dto.getParentId().equals(old.getParentId())) {
            //判断是否移动到自身下面
            if (dto.getParentId().equals(old.getId())) {
                return true;
            }
            //判断是否移动到子节点下
            List<T> children = findChildListByParentIds(old.getParentIds());
            for (T child : children) {
                if (child.getId().equals(dto.getParentId())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 日志操作类型
     */
    public enum OpType {
        login, insert, delete, update, select, logout
    }

    /** ---------------------------------------构建树 start------------------------------------------------------- */

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list 分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<T> getChilds(List<T> list, Long parentId) {
        List<T> returnList = new ArrayList<>();
        for (T t : list) {
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (parentId.equals(t.getParentId())) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }


    /**
     * 递归列表
     *
     * @param list
     * @param t
     */
    private void recursionFn(List<T> list, T t) {
        // 得到子节点列表
        List<T> childList = getChildList(list, t);
        t.setChildren(childList);
        for (T tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }


    /**
     * 得到子节点列表
     */
    private List<T> getChildList(List<T> list, T t) {
        List<T> tList = new ArrayList<>();
        for (T n : list) {
            if (t.getId().equals(n.getParentId())) {
                tList.add(n);
            }
        }
        return tList;
    }


    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<T> list, T t) {
        return getChildList(list, t).size() > 0;
    }

    /** ---------------------------------------构建树 end------------------------------------------------------- */



    /**
     * 是否存在子节点
     *
     * @param sysMenuId 部门ID
     * @return 结果
     */
    public boolean hasChildById(Long sysMenuId) {
        Long result = findChildCountById(sysMenuId);
        return result > 0;
    }

    public Long findChildCountById(Long id) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", id);
        return count(queryWrapper);
    }
}
